#pragma once

#include "Scheduler.hpp"
#include <iostream>
#include <memory>

class RuleManager {
    std::shared_ptr<Cppsched::Scheduler> _scheduler;

public:
    RuleManager(std::size_t threadNum)
        : _scheduler(std::make_shared<Cppsched::Scheduler>(threadNum)) {
    }

    template <typename F, typename... Args>
    void every(const std::string &taskId, const std::chrono::system_clock::duration &time, F &&f, Args &&... args) {
        try {
            _scheduler->every(taskId, time, std::forward<F>(f), std::forward<Args>(args)...);
        }
        catch (const std::exception &e) {
            std::cerr << "catch exception --> " << e.what() << std::endl;
        }
    }

    template <typename F, typename... Args>
    void at(const std::string &taskId, const std::chrono::system_clock::time_point &time, F &&f, Args &&... args) {
        try {
            _scheduler->at(taskId, time, std::forward<F>(f), std::forward<Args>(args)...);
        }
        catch (const std::exception &e) {
            std::cerr << "catch exception --> " << e.what() << std::endl;
        }
    }

    template <typename F, typename... Args>
    void everyStartWith(const std::string &taskId, const std::chrono::system_clock::duration &time, F &&f, Args &&... args) {
        auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(time).count();
        auto currentMs = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
        auto remainder = currentMs % ms;

        // std::async(std::launch::async, [&] {
        //     std::this_thread::sleep_until(std::chrono::system_clock::now() + std::chrono::milliseconds(ms - remainder));
        //     _scheduler->every(taskId, time, std::forward<F>(f), std::forward<Args>(args)...);
        // });
        auto callback = std::bind(std::forward<F>(f), std::forward<Args>(args)...);

        at(taskId, std::chrono::system_clock::now() + std::chrono::milliseconds(ms - remainder), [=] {
            callback();
            _scheduler->remove_task(taskId);
            _scheduler->every(taskId, time, f, args...);
        });
    }

    bool removeRule(const std::string &taskId) {
        return _scheduler->remove_task(taskId);
    }
};
